package com.naiterui.ehp.bs.im.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.emr.MedicalRecordIdBO;
import com.naiterui.ehp.bp.bo.im.MsgSendParamBO;
import com.naiterui.ehp.bp.bo.im.SessionDetailBO;
import com.naiterui.ehp.bp.bo.im.SessionParamBO;
import com.naiterui.ehp.bp.bo.patient.MsgMaPublishBatchParamBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.ImConstants;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.security.HeaderConstant;
import com.naiterui.ehp.bp.utils.biz.AppJumpProtocol;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bs.im.config.ListenerConfig;
import com.naiterui.ehp.bs.im.config.MessageConfig;
import com.naiterui.ehp.bs.im.config.MqttConnectionConfig;
import com.naiterui.ehp.bs.im.config.RemoterConfig;
import com.naiterui.ehp.bs.im.exception.ExceptionCodes;
import com.naiterui.ehp.bs.im.exception.NaiteruiIMException;
import com.naiterui.ehp.bs.im.feign.PatientFeginClient;
import com.naiterui.ehp.bs.im.network.mqtt.pool.MqttClientManager;
import com.naiterui.ehp.bs.im.network.protocl.Chater;
import com.naiterui.ehp.bs.im.network.protocl.ConsultPaidNotice;
import com.naiterui.ehp.bs.im.network.protocl.CustomizedMessage;
import com.naiterui.ehp.bs.im.network.protocl.CustomizedMessage.HrefContent;
import com.naiterui.ehp.bs.im.network.protocl.CustomizedMessage.HrefContent.Params;
import com.naiterui.ehp.bs.im.network.protocl.Disturb;
import com.naiterui.ehp.bs.im.network.protocl.Extend;
import com.naiterui.ehp.bs.im.network.protocl.FollowUpFillin;
import com.naiterui.ehp.bs.im.network.protocl.Message;
import com.naiterui.ehp.bs.im.network.protocl.Replay;
import com.naiterui.ehp.bs.im.network.protocl.Session;
import com.naiterui.ehp.bs.im.network.protocl.SessionDetails;
import com.naiterui.ehp.bs.im.network.protocl.SessionParams;
import com.naiterui.ehp.bs.im.network.protocl.Shield;
import com.naiterui.ehp.bs.im.network.transcode.IMessageTransCoder;
import com.naiterui.ehp.bs.im.repository.mongo.SessionDao;
import com.naiterui.ehp.bs.im.service.EventHandler;
import com.naiterui.ehp.bs.im.service.IMessageService;
import com.naiterui.ehp.bs.im.service.INaiteruiIMService;
import com.naiterui.ehp.bs.im.service.message.handler.MessageHandler;
import com.naiterui.ehp.bs.im.service.message.handler.ReplayHandler;
import com.naiterui.ehp.bs.im.service.message.handler.SessionHandler;
import com.naiterui.ehp.bs.im.service.message.push.DisturbHandler;
import com.naiterui.ehp.bs.im.service.message.push.ShieldHandler;
import com.naiterui.ehp.bs.im.utils.Constants;
import com.naiterui.ehp.bs.im.vo.ConsultInitVO;
import com.naiterui.ehp.bs.im.vo.DisturbVO;
import com.naiterui.ehp.bs.im.vo.ImgTimeGroupVO;
import com.naiterui.ehp.bs.im.vo.MediaUploadVO;
import com.naiterui.ehp.bs.im.vo.MessageHandleResult;
import com.naiterui.ehp.bs.im.vo.MessageVO;
import com.naiterui.ehp.bs.im.vo.MqttConnectParam;
import com.naiterui.ehp.bs.im.vo.PageVO;
import com.naiterui.ehp.bs.im.vo.QueryHistoryCondVO;
import com.naiterui.ehp.bs.im.vo.ReplayVO;
import com.naiterui.ehp.bs.im.vo.SessionVO;
import com.naiterui.ehp.bs.im.vo.ShieldVO;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;

@Service
@AllArgsConstructor
public class NaiteruiIMService implements INaiteruiIMService {

    private static final Logger LOGGER = LoggerFactory.getLogger(NaiteruiIMService.class);
    private final MessageHandler messageHandler;
    private final EventHandler eventHandler;
    private final MqttConnectionConfig mqttConnectionConfig;
    private final ListenerConfig listenerConfig;
    private final DisturbHandler disturbHandler;
    private final ShieldHandler shieldHandler;
    private final ReplayHandler replayHandler;
    private final IMessageTransCoder messageTransCoder;
    private final SessionHandler sessionHandler;
    private final MqttClientManager mqttClientManager;
    private final MessageConfig messageConfig;
    private final SessionDao sessionDao;
    private final RemoterConfig remoterConfig;
    private final PatientFeginClient patientFeginClient;
    private final IMessageService messageService;

    @Override
    public MqttConnectParam getMqttConnectionParams(int origin, Long userId, String token, String ip) throws NaiteruiIMException {
        MqttConnectParam mcp = new MqttConnectParam();

        mcp.setKeepAliveInterval(this.mqttConnectionConfig.getClientKeepAliveInterval());
        mcp.setCleanSession(this.mqttConnectionConfig.isClientCleanSession());
        mcp.setStatusTopicName(this.listenerConfig.getStatusLsnTopic());
        mcp.setWillTopicName(this.listenerConfig.getWillLsnTopic());
        mcp.setMsgTopicName(this.mqttConnectionConfig.getClientMsgTopicName());
        mcp.setConnectionTimeout(this.mqttConnectionConfig.getClientConnectionTimeout());
        mcp.setQos(this.mqttConnectionConfig.getClientQos());
        mcp.setRetained(this.mqttConnectionConfig.isClientRetained());
        mcp.setTraceHost(this.mqttConnectionConfig.getTraceHost());
        mcp.setSessionTimeout(this.mqttConnectionConfig.getSessionTimeout());
        mcp.setSessionTimeout(this.mqttConnectionConfig.getSessionTimeout());
        // TODO 待修改 By Gaoll
        if (origin == HeaderConstant.HEADER_ORIGIN_DOCTOR_APP) {
            mcp.setMsgTopicName("dr_" + userId);
            mcp.setPublicTopicName("dr_publicTopic" + userId);
            mcp.setPublicPushTopicName("dr_publicPush" + userId);
            mcp.setPrivatePushTopicName("dr_" + userId + "/#");
        } else if (origin == HeaderConstant.HEADER_ORIGIN_PATIENT_APP) {
            mcp.setMsgTopicName("pt_" + userId);
            mcp.setPublicTopicName("pt_publicTopic");
            mcp.setPublicPushTopicName("pt_publicPush");
            mcp.setPrivatePushTopicName("pt_" + userId + "/#");
        } else if (origin == HeaderConstant.HEADER_ORIGIN_DOCTOR_PC) {
            mcp.setMsgTopicName("dr_" + userId);
            mcp.setPublicTopicName("dr_publicTopic" + userId);
            mcp.setPublicPushTopicName("dr_publicPush" + userId);
            mcp.setPrivatePushTopicName("dr_" + userId + "/notice");
        }

        try {
            // 根据clientId获取节点
            String serverURI = this.mqttClientManager.getNodeForKey(mcp.getMsgTopicName() + userId);
            String targetURI = this.convertServerURI(serverURI);
            String[] strs = targetURI.replace("tcp://", "").split(":");
            String host = strs[0];
            int port = NumberUtils.toInt(strs[1]);
            mcp.setDnHost(this.mqttConnectionConfig.getHost());
            mcp.setHost(host);
            mcp.setPort(port);
            LOGGER.info("origin={},userId={},connectParams={}", origin, userId, mcp.toString());
        } catch (Exception e) {
            throw new NaiteruiIMException(ExceptionCodes.FAILED);
        }

        return mcp;
    }

    private String convertServerURI(String serverURI) {
        int index = Arrays.asList(this.mqttConnectionConfig.getInnerServers()).indexOf(serverURI);
        return Arrays.asList(this.mqttConnectionConfig.getServers()).get(index);
    }

    @Override
    public MessageVO sendMessage(int origin, String message) throws BusinessException {

        LOGGER.debug("received a http send message request, the content is {}", message);

        // 解码消息对象
        Message<?> messageBean = this.messageTransCoder.decodeMessage(message);

        // 校准消息发送时间
        messageBean.setSendTime(System.currentTimeMillis());

        // 设置消息来源
        messageBean.setRelation((origin == HeaderConstant.HEADER_ORIGIN_PATIENT_APP || origin == HeaderConstant.HEADER_ORIGIN_PATIENT_WX
                || origin == HeaderConstant.HEADER_ORIGIN_YUNHOSPITAL_PC) ? Constants.MESSAGE_RESOURCE_PATIENT : Constants.MESSAGE_RESOURCE_DOCTOR);

        // 消息来自医生端
        if (messageBean.getRelation() == Constants.MESSAGE_RESOURCE_DOCTOR) {
            // 判断当前患者是否被冻结
            Object sourceStatus = RedisUtil.valueOps().getObject(Constants.CACHE_KEY_DIS_PATIENT + messageBean.getTo().getId());
            int status = sourceStatus == null ? 0 : NumberUtils.toInt(sourceStatus.toString());
            // 账号状态(1,禁用；0,正常)
            if (status == 1) {
                throw new NaiteruiIMException(ExceptionCodes.PATIENT_DISABLED);
            }
        }
        // 消息来自患者端
        else {
            Long patientId = messageBean.getFrom().getId();
            Long doctorId = messageBean.getTo().getId();
            boolean isFirstBind = RedisUtil.keyOps().existsKey(CommonConstant.PATIENT_PERSONAL_DOCTOR_FIRST_BIND + doctorId + "_" + patientId);
            boolean sessionKey = RedisUtil.keyOps().existsKey(doctorId + "_" + patientId);
            // 不是首次绑定并且没有进行中的会话
            if (!isFirstBind && !sessionKey) {
                throw new NaiteruiIMException(ExceptionCodes.CONSULT_NOT_SESSION);
            }
            messageBean.setLastPlatform(origin);
            // 验证患者账户状态
            Object sourceStatus = RedisUtil.valueOps().getObject(Constants.CACHE_KEY_DIS_PATIENT + patientId);
            int status = sourceStatus == null ? 0 : NumberUtils.toInt(sourceStatus.toString());
            // 账号状态(1,禁用；0,正常)
            if (status == 1) {
                throw new NaiteruiIMException(ExceptionCodes.PATIENT_ACCOUNT_ERROR);
            }

            // 验证医生账户状态
            String hashKey = Constants.CACHT_KEY_INFO_DOCTOR + doctorId;
            String fieldsKey = Constants.DOCTOR_STATUS_KEY;
            Object cacheStauts = RedisUtil.hashOps().hget(hashKey, fieldsKey);
            if ("1".equals(cacheStauts)) {
                throw new NaiteruiIMException(ExceptionCodes.DOCTOR_IS_DISABLE);
            }
        }

        /*
         * 判断是否开启免费会话,创建,过滤,session处理
         */
        boolean ignoreCharge = messageBean.getExd() != null && (messageBean.getExd().getIgnoreCharge() == null ? false : messageBean.getExd().getIgnoreCharge());
        SessionVO vo = this.sessionHandler.handleSession(messageBean, ignoreCharge);
        if(vo != null){
            String inquirerIdKey;
            if (messageBean.getRelation() == Constants.MESSAGE_RESOURCE_DOCTOR) {
                inquirerIdKey = String.join("_", messageBean.getFrom().getId().toString(), messageBean.getTo().getId().toString());
            } else {
                inquirerIdKey = String.join("_", messageBean.getTo().getId().toString(), messageBean.getFrom().getId().toString());
            }
            // 处理消息就诊人字段
            String inquirerIdStr = RedisUtil.hashOps().hget(CommonConstant.CONSULT_PATIENT_AIM_INQUIRER, inquirerIdKey);
            LOGGER.info("inquirer id str:{}", inquirerIdStr);
            messageBean.setInquirerId(StrUtil.isBlank(inquirerIdStr) ? null : Long.parseLong(inquirerIdStr));
        }
        LOGGER.info("usendmessage sessionvo:{}", JSONUtil.toJsonStr(vo));

        // 返回结果
        MessageVO resultVO = new MessageVO();
        // 支付失败或者未确认支付结果情况下返回给前端
        if (vo != null && vo.getConsultPayStatus() != null) {
            if (vo.getConsultPayStatus() == Constants.SESSION_PAY_STATUS_FAIL || vo.getConsultPayStatus() == Constants.SESSION_PAY_STATUS_PAYMENT_IN) {
                resultVO.setConsultSessionId(vo.getConsultSessionId());
                resultVO.setDoctorName(vo.getDoctorName());
                resultVO.setConsultPrice(vo.getConsultPrice());
                resultVO.setConsultPayStatus(vo.getConsultPayStatus());
                return resultVO;
            }
        }

        // 如果是会话已结束直接返回结束时间
        if (vo != null && vo.isFinished()) {
            resultVO.setEndTime(vo.getEndTime());
            return resultVO;
        }

        LOGGER.info("sendmessage messageBean:{}", JSONUtil.toJsonStr(messageBean));

        // 消息存储、离线消息、消息发送、咨询积分处理等逻辑
        MessageHandleResult handleResult = this.messageHandler.handleMessage(messageBean);

        /*
         *如果是患者开启会话则,检索患者相关的最新的一个病历发送给医生
         */
        if (messageBean.getRelation() == Constants.MESSAGE_RESOURCE_PATIENT
                && !RedisUtil.keyOps().existsKey(Constants.CACHE_PATIENT_EXSIT_MESSAGE_IN_A_SESSION
                    + messageBean.getTo().getId() + "_" + messageBean.getFrom().getId())) {
            MedicalRecordIdBO medicalRecordIdBo = null;
            /*try {
                medicalRecordIdBo = this.remoter.queryPatientsLastMedicalRecord(messageBean.getTo().getId(), messageBean.getFrom().getId());
            } catch (Exception e) {
                LOGGER.error("检索患者相关的最新的一个病历异常");
            }*/
            Message<CustomizedMessage> medicalRecordReminder = new Message();
            medicalRecordReminder.setSession(messageBean.getSession());
            medicalRecordReminder.setFrom(messageBean.getFrom());
            medicalRecordReminder.setTo(messageBean.getTo());
            medicalRecordReminder.setRelation(Constants.MESSAGE_RESOURCE_PATIENT);
            medicalRecordReminder.setSendTime(System.currentTimeMillis());
            medicalRecordReminder.setSysMsgType(Message.SYS_MSG_TYPE_SYSTEM);
            medicalRecordReminder.setVisualType(Message.MESSAGE_VISUAL_TYPE_DOCTOR);
            Extend exd = new Extend();
            exd.setPersistible(false);
            if (medicalRecordIdBo != null && medicalRecordIdBo.getIdFlag() == MedicalRecordIdBO.IDFLAG_NO) {// 如果不存在则提醒填写病历
                medicalRecordReminder.setType(Constants.MESSAGE_TYPE_REMIND_PATIENTS_FILL_MEDICAL_RECORD);
                medicalRecordReminder.setExd(exd);
                medicalRecordReminder.setContent(this.createRemindPatientToFillMedicalRecordMessage(messageBean.getFrom().getId()));
                this.messageHandler.handleMessage(medicalRecordReminder);
            } else if (medicalRecordIdBo != null && medicalRecordIdBo.getIdFlag() == MedicalRecordIdBO.IDFLAG_YES) {
                medicalRecordReminder.setType(Constants.MESSAGE_TYPE_PATIENTS_LAST_MEDICAL_RECORD);
                medicalRecordReminder.setExd(exd);
                medicalRecordReminder.setContent(this.createPatientsLastMedicalRecordMessage(medicalRecordIdBo));
                this.messageHandler.handleMessage(medicalRecordReminder);// 发送最近一份病历消息
            }
            /*
             * 设置单次会话内已发送过病历的缓存
             */
            RedisUtil.valueOps().set(Constants.CACHE_PATIENT_EXSIT_MESSAGE_IN_A_SESSION + messageBean.getTo().getId() + "_" + messageBean.getFrom().getId(),
                          medicalRecordReminder.getSendTime(), this.mqttConnectionConfig.getSessionTimeout() / 1000);
        }
        if (vo != null) {
            resultVO.setSessionId(vo.getSessionId());
            resultVO.setConsultSessionId(vo.getConsultSessionId());
            resultVO.setBeginTime(vo.getBeginTime());
            resultVO.setIsFirst(vo.getIsFirst());
            resultVO.setConsultSourceType(vo.getConsultSourceType());
        }
        resultVO.setDrugNames(handleResult.getDrugNames());
        resultVO.setSendTime(messageBean.getSendTime());
        resultVO.setRecommandId(handleResult.getRecommandId());
        resultVO.setSerialNumber(handleResult.getSerialNumber());
        resultVO.setRecommandStatus(handleResult.getRecommandStatus());
        resultVO.setId(messageBean.getId());
        return resultVO;
    }

    @Override
    public String uploadMediaData(MediaUploadVO mediaUpload, MultipartFile[] file) throws NaiteruiIMException {
        // 文件附件
        MultipartFile targetfile = null;
        if (file == null || file.length == 0) {
            LOGGER.error("not found any attachment!");
            throw new NaiteruiIMException(ExceptionCodes.MEDIA_UPLOAD_NO_ATTACHMENT);
        }
        targetfile = file[0];
        // 检测参数的完整性
        if (!mediaUpload.validate()) {
            LOGGER.error("upload parameters not incorrect or incomplete, {}", mediaUpload);
            throw new NaiteruiIMException(ExceptionCodes.INVALID_REQUEST);
        }

        LOGGER.info("upload media platform is, {}", mediaUpload.getPlatform());
        // 文件下载的完整地址
        return this.messageHandler.persistentMediaFile(mediaUpload, targetfile);
    }

    @Override
    public PageVO getChatHistory(int origin, QueryHistoryCondVO vo) throws BusinessException {

        // 参数检测
        if (vo.getFromId() == null || vo.getFromId() <= 0 || vo.getToId() == null || vo.getToId() <= 0) {
            LOGGER.error("required parameters not found, please check it!");
            throw new NaiteruiIMException(ExceptionCodes.INVALID_REQUEST);
        }

        return this.messageHandler.getChatHistory(origin, vo);
    }

    @Override
    public PageVO<ImgTimeGroupVO> getImageUploadHistory(String userSign, Long patientId, Long doctorId, Integer page, Integer num)
            throws NaiteruiIMException {

        // 参数检测
        if (StringUtils.isEmpty(userSign)) {
            LOGGER.error("need parameter userSign");
            throw new NaiteruiIMException(ExceptionCodes.INVALID_REQUEST);
        }
        if (patientId == null || patientId <= 0) {
            LOGGER.error("need patientId");
            throw new NaiteruiIMException(ExceptionCodes.INVALID_REQUEST);
        }

        // 设置默认值
        page = (page == null || page <= 0) ? PageVO.DEFAULT_PAGE : page;
        num = (num == null || num <= 0) ? PageVO.DEFAULT_NUM : num;

        return this.messageHandler.getImageUploadHistory(patientId, doctorId, page, num);
    }

    @Override
    public int checkOrigin(String originHeader) throws NaiteruiIMException {

        // 判断平台参数是否指定
        if (StringUtils.isEmpty(originHeader)) {
            LOGGER.error("not found origin in http header");
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        // 判断平台参数是否合法
        if (!StringUtils.isNumeric(originHeader)) {
            LOGGER.error("parameter [origin] is invalid, origin is {}", originHeader);
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        // 判断origin是否合法
        /* 0：患者App,1：医生App,2：患者微信,3：互联网医院(PC)*/
        int origin = Integer.parseInt(originHeader);
        if (origin != HeaderConstant.HEADER_ORIGIN_DOCTOR_APP && origin != HeaderConstant.HEADER_ORIGIN_PATIENT_APP
            && origin != HeaderConstant.HEADER_ORIGIN_PATIENT_WX
            && origin != HeaderConstant.HEADER_ORIGIN_YUNHOSPITAL_PC
            && origin != HeaderConstant.HEADER_ORIGIN_DOCTOR_PC) {
            LOGGER.error("parameter [origin] is invalid, origin is {}", origin);
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        } else {
            return origin;
        }
    }

    @Override
    public int checkPlatform(String platformHeader) throws NaiteruiIMException {

        // 判断平台参数是否指定
        if (StringUtils.isEmpty(platformHeader)) {
            LOGGER.error("not found platform in http header");
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        // 判断平台参数是否合法
        if (!StringUtils.isNumeric(platformHeader)) {
            LOGGER.error("parameter [platform] is invalid, platform is {}", platformHeader);
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        // 判断origin是否合法
        /* 0：患者App,1：医生App,2：患者微信,3：互联网医院(PC)*/
        int platform = Integer.parseInt(platformHeader);
        if (platform != HeaderConstant.HEADER_PLATFORM_ANDROID_PHONE && platform != HeaderConstant.HEADER_PLATFORM_IOS_PHONE
                && platform != HeaderConstant.HEADER_PLATFORM_PC) {
            LOGGER.error("parameter [platform] is invalid, platform is {}", platform);
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        } else {
            return platform;
        }
    }

    @Override
    public void setDisturb(Disturb disturb, int operate) {
        this.disturbHandler.setDisturb(disturb, operate);
    }

    @Override
    public DisturbVO getDisturb(Long userId) {
        return this.disturbHandler.getDisturb(userId);
    }

    @Override
    public void setShield(Shield shield, int operate) throws NaiteruiIMException {
        if (!shield.selfValidate()) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }
        this.shieldHandler.setShield(shield, operate);
    }

    @Override
    public ShieldVO getShield(Long userId) {
        return this.shieldHandler.getShield(userId);
    }

    @Override
    public ReplayVO setOrGetReplay(Replay replay, Integer opreate) throws NaiteruiIMException {
        // 验证字段
        if (!replay.selfValidate()) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }
        return this.replayHandler.setOrGetReplay(replay, opreate);
    }

    @Override
    public PageVO<?> findSessionPageList(SessionParams query) throws NaiteruiIMException {
        if (!query.selfValidate()) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        return this.sessionHandler.findSessionPageList(query);
    }

    @Override
    public PageVO<?> findMessagesBySession(String sessionId, Integer pageNo, Integer pageSize, Long endTime, Integer origin, Long doctorId, Long patientId)
            throws NaiteruiIMException {
        // 验证字段
        if (pageNo == null || pageSize == null || null == origin) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        return this.messageHandler.findMessageBySession(sessionId, pageNo, pageSize, endTime, origin, doctorId, patientId);
    }

    @Override
    public SessionVO finishSession(SessionParams params) throws NaiteruiIMException {
        // 验证字段
        if (StringUtils.isBlank(params.getSessionId()) || params.getDoctorId() == null || params.getPatientId() == null
                || params.getFreeFlag() == null) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        SessionDetails sessionDetails = this.sessionDao.findSessionById(params.getSessionId());

        /*Boolean hadUpdate = this.remoter.whetherDoctorUpdateMedicalRecord(params.getDoctorId(), params.getPatientId(), sessionDetails.getBeginTime(), System.currentTimeMillis());
        if (!hadUpdate) {
            *//*
         * 结束会话钱判断是否需要向医生推送更新病例提醒消息
         *//*
            this.sendDoctorReminderToUpdateMedicalRecord(params.getDoctorId(), params.getPatientId(), sessionDetails);
            throw new NaiteruiIMException(ExceptionCodes.SESSION_NEEDS_MEDICAL_RECODE);
        }*/

        // 结束会话
        Long endTime = this.sessionHandler.finishSession(params.getSessionId(),
                                                         params.getDoctorId(),
                                                         params.getPatientId(),
                                                         params.getFreeFlag(),
                                                         true,
                                                         false,
                                                         sessionDetails,
                                                         Constants.SESSION_CLOSE_TRIGGER_SOURCE_DOCTOR_MANUAL);

        SessionVO vo = new SessionVO();
        vo.setEndTime(endTime);

        return vo;
    }

    private CustomizedMessage createRemindDoctorUpdateRecordMessage(Long patientId) {
        CustomizedMessage customizedMessage = new CustomizedMessage();
        CustomizedMessage.HrefContent hrefContent = new CustomizedMessage.HrefContent();
        hrefContent.setK(AppJumpProtocol.IM_UPDATE_MEDICAL_RECORD_REMIND_TO_DOCTOR.getK());
        CustomizedMessage.HrefContent.Params patientIdParam = new HrefContent.Params("patientId", patientId.toString());
        List<Params> paramsList = new ArrayList<>();
        paramsList.add(patientIdParam);
        hrefContent.setParams(paramsList);
        String text = String.format(this.messageConfig.getRemindDoctorUpdateMedicalRecord(), JSON.toJSONString(hrefContent));// text
        customizedMessage.setText(text);
        return customizedMessage;
    }

    @Override
    public void sessionPaid(String sessionId) throws NaiteruiIMException {
        // 验证字段
        if (StringUtils.isEmpty(sessionId)) {
            throw new NaiteruiIMException(ExceptionCodes.MSG_NOT_COMPLETE);
        }

        this.sessionHandler.sessionPaid(sessionId);
    }

    @Override
    public SessionVO findSessionStatus(String sessionId) throws NaiteruiIMException {
        // 更新付款类型
        return this.sessionHandler.findSessionStatus(sessionId);
    }

    @Override
    public void publicNotice(String message, String userType) {
        this.eventHandler.publicNotice(message, userType);
    }

    @Override
    public void privateNotice(String message, Long userId, Boolean force, String userType) {
        this.eventHandler.privateNotice(message, userId, force, userType);
    }

    @Override
    public SessionVO createConsultSessionAndNotice(ConsultInitVO consultInitVO) throws BusinessException {
        SessionVO sessionVo = null;
        /*
         *判断当前是否有进行中的会话
         */
        String sessionKey = SessionHandler.getSessionKey(consultInitVO.getDoctorId(), consultInitVO.getPatientId());
        if (!RedisUtil.hashOps().hexist(sessionKey, "sessionId")) {
            /*
             * 创建会话
             */
            sessionVo = this.sessionHandler.createConsultSession(consultInitVO.getDoctorId(), consultInitVO.getPatientId());

        } else {// 已经存在会话的情况
            sessionVo = new SessionVO();
            sessionVo.setSessionId(RedisUtil.hashOps().hget(sessionKey, "sessionId"));
            sessionVo.setConsultSessionId(Long.parseLong(RedisUtil.hashOps().hget(sessionKey, "consultSessionId")));
            sessionVo.setBeginTime(Long.parseLong(RedisUtil.hashOps().hget(sessionKey, "beginTime")));
        }

        // 会话逻辑处理后,优先添加Redis标示,对接口超时调用方获取
        RedisUtil.hashOps().hset(CommonConstant.PAY_CONSULT_SESSION_KEY + consultInitVO.getOrderSn(), "sessionId", sessionVo.getSessionId());
        RedisUtil.hashOps().hset(CommonConstant.PAY_CONSULT_SESSION_KEY + consultInitVO.getOrderSn(), "consultSessionId",
                                 sessionVo.getConsultSessionId() + "");

        /*
         * 向医生端推送图文咨询付费完成后的消息
         */
        boolean toDoctor = this.sendNoticeMessageToDoc(sessionVo, consultInitVO);

        /*
         * 判断是否有待发送病历消息
         */
        String caseId = RedisUtil.valueOps()
                                 .getString(
                                         Constants.CACHE_PATIENTS_MEDICAL_RECORD_TOSEND_KEY + consultInitVO.getPatientId() + "_" + consultInitVO.getDoctorId());
        if (caseId != null) {
            this.messageService.sendPatientsMedicalRecord(consultInitVO.getDoctorId(), consultInitVO.getPatientId(), caseId, sessionVo);
        } else {
            /*
             * 向患者端发送图文咨询付费完成后的消息
             */
            // 2019/11/27 9:06 下午 高亮亮 消息发送前必填病历消息，支付信息忽略
            //boolean toPatient = this.sendNoticeMessageToPt(sessionVo, consultInitVO);
        }

        return sessionVo;
    }

    @Override
    public void sendMultipleMessage(String message, String toIds) throws NaiteruiIMException {
        String content;
        Message<?> messageBean = NaiteruiIMService.this.messageTransCoder.decodeMessage(message);
        if (messageBean.getType() == Constants.MESSAGE_TYPE_TEXT) {
            content = (String) messageBean.getContent();
        } else {
            content = "来自医生的医嘱提醒";
        }
        this.patientFeginClient.publishBatchMaMessage(MsgMaPublishBatchParamBO.builder()
                                                                          .content(content)
                                                                          .doctorId(messageBean.getFrom().getId())
                                                                          .patientIds(StringUtil.convertLongStr2Set(toIds, ","))
                                                                          .build());
    }

    @Override
    public PageVO<SessionDetailBO> sessionPageList(SessionParamBO params) throws NaiteruiIMException {
        return this.sessionHandler.sessionPageList(params);
    }

    private boolean sendNoticeMessageToDoc(SessionVO sessionVo, ConsultInitVO consultInitVO) {

        boolean result = false;
        try {

            if (consultInitVO.getPayAmount() == null) {
                LOGGER.error("sendNoticeMessageToDoc，患者支付会话后构建向医生的发送通知 支付金额异常,payAmount：{}", consultInitVO.getPayAmount());
                return result;
            }

            Message<ConsultPaidNotice> message = new Message<>();

            Chater from = new Chater();
            from.setId(consultInitVO.getPatientId());
            Chater to = new Chater();
            to.setId(consultInitVO.getDoctorId());

            String paidMoney = new BigDecimal(consultInitVO.getPayAmount()).divide(new BigDecimal(100)).toString();// 患者支付金额
            ConsultPaidNotice paidNotice = new ConsultPaidNotice();
            paidNotice.setDetail(String.format(this.messageConfig.getConsultPaidNoticeToDoc(), consultInitVO.getPatientName(),
                                             DateUtil.formatDate(new Date(System.currentTimeMillis()), DateUtil.MONTH_DAY_HH_DD_FORMAT), paidMoney));
            paidNotice.setPrice(consultInitVO.getPayAmount().longValue());

            message.setType(Constants.MESSAGE_TYPE_PAID_CONSULT);
            message.setFrom(from);
            message.setTo(to);
            message.setContent(paidNotice);
            message.setRelation(Constants.MESSAGE_RESOURCE_PATIENT);
            message.setSendTime(System.currentTimeMillis());
            message.setVisualType(Message.MESSAGE_VISUAL_TYPE_DOCTOR);
            message.setSession(new Session());
            message.getSession().setSessionId(sessionVo.getSessionId());
            message.getSession().setBeginTime(sessionVo.getBeginTime());
            String sessionKey = SessionHandler.getSessionKey(consultInitVO.getDoctorId(), consultInitVO.getPatientId());
            Integer payType = NumberUtils.toInt(RedisUtil.hashOps().hget(sessionKey, "payType"));
            Integer consultPayType = NumberUtils.toInt(RedisUtil.hashOps().hget(sessionKey, "consultPayType"));
            message.getSession().setPayType(payType);
            message.getSession().setConsultPayType(consultPayType);// 付费咨询是否需要付费

            this.messageHandler.handleMessage(message);

            result = true;
        } catch (Exception e) {
            LOGGER.error("患者支付付费咨询后向医生发送的消息内容 ERROR ", e);
        }

        return result;
    }

    private boolean sendNoticeMessageToPt(SessionVO sessionVo, ConsultInitVO consultInitVO) {

        boolean result = false;
        try {

            if (consultInitVO.getPayAmount() == null) {
                LOGGER.error("sendNoticeMessageToDoc，患者支付会话后构建向患者的发送通知 支付金额异常,payAmount：{}", consultInitVO.getPayAmount());
                return result;
            }

            Message<ConsultPaidNotice> message = new Message<>();

            Chater from = new Chater();
            from.setId(consultInitVO.getDoctorId());
            Chater to = new Chater();
            to.setId(consultInitVO.getPatientId());

            String paidMoney = new BigDecimal(consultInitVO.getPayAmount()).divide(new BigDecimal(100)).toString();// 患者支付金额
            ConsultPaidNotice paidNotice = new ConsultPaidNotice();
            paidNotice.setDetail(String.format(String.format(this.messageConfig.getConsultPaidNoticeToPt(), paidMoney,
                                                             consultInitVO.getDoctorName())));
            paidNotice.setPrice(consultInitVO.getPayAmount().longValue());

            message.setType(Constants.MESSAGE_TYPE_PAID_CONSULT);
            message.setFrom(from);
            message.setTo(to);
            message.setContent(paidNotice);
            message.setRelation(Constants.MESSAGE_RESOURCE_DOCTOR);
            message.setSendTime(System.currentTimeMillis());
            message.setVisualType(Message.MESSAGE_VISUAL_TYPE_PATIENT);
            message.setSession(new Session());
            message.getSession().setSessionId(sessionVo.getSessionId());
            message.getSession().setBeginTime(sessionVo.getBeginTime());
            String sessionKey = SessionHandler.getSessionKey(consultInitVO.getDoctorId(), consultInitVO.getPatientId());
            Integer payType = NumberUtils.toInt(RedisUtil.hashOps().hget(sessionKey, "payType"));
            Integer consultPayType = NumberUtils.toInt(RedisUtil.hashOps().hget(sessionKey, "consultPayType"));
            message.getSession().setPayType(payType);
            message.getSession().setConsultPayType(consultPayType);// 付费咨询是否需要付费

            this.messageHandler.handleMessage(message);

            result = true;
        } catch (Exception e) {
            LOGGER.error("患者支付付费咨询后向患者发送的消息内容 ERROR ", e);
        }

        return result;
    }

    private CustomizedMessage createPatientsLastMedicalRecordMessage(MedicalRecordIdBO medicalRecordIdBo) {
        CustomizedMessage customizedMessage = new CustomizedMessage();
        CustomizedMessage.HrefContent hrefContent = new CustomizedMessage.HrefContent();
        CustomizedMessage.HrefContent.Params params = new HrefContent.Params("recoreId", medicalRecordIdBo.getId());
        List<Params> paramsList = new ArrayList<>();
        paramsList.add(params);
        hrefContent.setParams(paramsList);
        hrefContent.setK(AppJumpProtocol.IM_PATIENTS_MEDICAL_RECORD_DETAIL.getK());
        hrefContent.setV(this.remoterConfig.getPatientsMedicalDetailUrl());
        String text = String.format(this.messageConfig.getPatientsLastMedicalRecordReminder(), medicalRecordIdBo.getCreateDate(),
                                    JSON.toJSONString(hrefContent));// text
        customizedMessage.setText(text);
        return customizedMessage;
    }

    private CustomizedMessage createRemindPatientToFillMedicalRecordMessage(Long patientId) {
        CustomizedMessage customizedMessage = new CustomizedMessage();
        CustomizedMessage.HrefContent hrefContent = new CustomizedMessage.HrefContent();
        CustomizedMessage.HrefContent.Params params = new HrefContent.Params("patientId", patientId.toString());
        List<Params> paramsList = new ArrayList<>();
        paramsList.add(params);
        hrefContent.setParams(paramsList);
        hrefContent.setK(AppJumpProtocol.IM_FILL_MEDICAL_RECORD_REMIND.getK());
        hrefContent.setV(this.remoterConfig.getRemindPatientFillMedicalRecordUrl());
        String text = String.format(this.messageConfig.getRemindPatientsFillMedicalRecord(), JSON.toJSONString(hrefContent));// text
        customizedMessage.setText(text);
        return customizedMessage;
    }

    @Override
    public void sendSysMessage(MsgSendParamBO msgSendParamBO) throws BusinessException {
        Long doctorId = msgSendParamBO.getDoctorId();
        Long patientId = msgSendParamBO.getPatientId();
        Integer relation = msgSendParamBO.getRelation();
        Chater from = new Chater();
        from.setId(relation == ImConstants.MESSAGE_RESOURCE_DOCTOR ?  doctorId : patientId);
        Chater to = new Chater();
        to.setId(relation == ImConstants.MESSAGE_RESOURCE_DOCTOR ? patientId : doctorId);
        Message message = new Message<String>();
        message.setContent(msgSendParamBO.getContent());
        message.setFrom(from);
        message.setTo(to);
        message.setType(msgSendParamBO.getMsgType());
        message.setVisualType(msgSendParamBO.getVisualType());
        message.setInquirerId(msgSendParamBO.getInquirerId());
        message.setRelation(relation);
        message.setSendTime(System.currentTimeMillis());
        message.setSysMsgType(msgSendParamBO.getSysMsgType());
        message.setVisualType(message.getVisualType());
        message.setLastPlatform(msgSendParamBO.getLastPlatform());
        if (msgSendParamBO.getMsgType() == ImConstants.MESSAGE_TYPE_FOLLOW_UP_FILLIN) {
            // 转换一下
            message.setContent(JSONUtil.toBean(JSONUtil.toJsonStr(message.getContent()), FollowUpFillin.class));
            LOGGER.info("发送系统消息, param: {}", JSONUtil.toJsonStr(message));
            this.messageHandler.handleMessage(message);
            return;
        }
        LOGGER.info("发送系统消息, param: {}", JSONUtil.toJsonStr(message));
//        // 保存消息到聊天记录
//        messageDao.saveMessage(message);
        // 如果消息来源是医生端
        if (relation == ImConstants.MESSAGE_RESOURCE_DOCTOR) {
            this.sendMessage(HeaderConstant.HEADER_ORIGIN_DOCTOR_APP, JSONUtil.toJsonStr(message));
//            doctorMessageHandler.sendMessage(message);
        } else {
            this.sendMessage(HeaderConstant.HEADER_ORIGIN_PATIENT_APP, JSONUtil.toJsonStr(message));
//            patientMessageHandler.sendMessage(message);
        }
    }

}
